﻿using DataConvertLib;
using System;

namespace YWM.Communication
{
    /// <summary>
    /// ModBusTCP工具
    /// </summary>
    public static class ModBusTCPUtil
    {
        #region 获取请求消息

        #region 读取

        /// <summary>
        /// 获取读输出线圈请求消息
        /// </summary>
        /// <param name="transactionId">事物处理标识</param>
        /// <param name="addr">线圈起始地址</param>
        /// <param name="count">读取数量</param>
        /// <param name="unitId">单元标识符</param>
        /// <returns></returns>
        public static (byte[], int) Get0x01RequestMessage(short transactionId, short addr, short count, byte unitId = 1)
        {
            return GetReadRequestMessage(transactionId, ModBusFunction.Read01, addr, count, unitId);
        }

        /// <summary>
        /// 获取读输入线圈请求消息
        /// </summary>
        /// <param name="transactionId">事物处理标识</param>
        /// <param name="addr">线圈起始地址</param>
        /// <param name="count">读取数量</param>
        /// <param name="unitId">单元标识符</param>
        /// <returns></returns>
        public static (byte[], int) Get0x02RequestMessage(short transactionId, short addr, short count, byte unitId = 1)
        {
            return GetReadRequestMessage(transactionId, ModBusFunction.Read02, addr, count, unitId);
        }

        /// <summary>
        /// 获取读输出寄存器请求消息
        /// </summary>
        /// <param name="transactionId">事物处理标识</param>
        /// <param name="addr">寄存器起始地址</param>
        /// <param name="count">读取数量</param>
        /// <param name="unitId">单元标识符</param>
        /// <returns></returns>
        public static (byte[], int) Get0x03RequestMessage(short transactionId, short addr, short count, byte unitId = 1)
        {
            return GetReadRequestMessage(transactionId, ModBusFunction.Read03, addr, count, unitId);
        }

        /// <summary>
        /// 获取读输入寄存器请求消息
        /// </summary>
        /// <param name="transactionId">事物处理标识</param>
        /// <param name="addr">寄存器起始地址</param>
        /// <param name="count">读取数量</param>
        /// <param name="unitId">单元标识符</param>
        /// <returns></returns>
        public static (byte[], int) Get0x04RequestMessage(short transactionId, short addr, short count, byte unitId = 1)
        {
            return GetReadRequestMessage(transactionId, ModBusFunction.Read04, addr, count, unitId);
        }

        /// <summary>
        /// 获取读请求信息
        /// </summary>
        /// <param name="transactionId">事物处理标识</param>
        /// <param name="modBusFunction">功能码</param>
        /// <param name="addr">寄存器起始地址</param>
        /// <param name="count">读取数量</param>
        /// <param name="unitId">单元标识符</param>
        /// <returns></returns>
        public static (byte[], int) GetReadRequestMessage(short transactionId, ModBusFunction modBusFunction, short addr, short count, byte unitId = 1)
        {
            byte[] ends = { (byte)(count >> 8), (byte)count };
            byte function = (byte)modBusFunction;
            int responseCount = 9;
            if (function > 2)
            {
                responseCount += count * 2;
            }
            else
            {
                responseCount += count / 8 + (count % 8 == 0 ? 0 : 1);
            }
            return (GetFinallyRequestMessage(transactionId, unitId, modBusFunction, addr, ends), responseCount);
        }
        #endregion

        #region 写入
        /// <summary>
        /// 获取写单个线圈请求消息
        /// </summary>
        /// <param name="transactionId">事物处理标识</param>
        /// <param name="addr">线圈地址</param>
        /// <param name="isSet">是否置位线圈</param>
        /// <param name="unitId">单元标识符</param>
        /// <returns></returns>
        public static (byte[], int) Get0x05RequestMessage(short transactionId, short addr, bool isSet, byte unitId = 1)
        {
            byte[] ends = { (byte)(isSet ? 0xFF : 0x00), 0x00 };
            byte[] data = GetFinallyRequestMessage(transactionId, unitId, ModBusFunction.Wirte05, addr, ends);
            return (data, data.Length);
        }

        /// <summary>
        /// 获取写单个寄存器请求消息
        /// </summary>
        /// <param name="transactionId">事物处理标识</param>
        /// <param name="addr">寄存器地址</param>
        /// <param name="value">单个寄存器的值</param>
        /// <param name="unitId">单元标识符</param>
        /// <returns></returns>
        public static (byte[], int) Get0x06RequestMessage(short transactionId, short addr, short value, byte unitId = 1)
        {
            byte[] ends = ByteArrayLib.GetByteArrayFromShort(value);
            return Get0x06RequestMessage(transactionId, addr, ends, unitId);
        }

        /// <summary>
        /// 获取写单个寄存器请求消息
        /// </summary>
        /// <param name="transactionId">事物处理标识</param>
        /// <param name="addr">寄存器地址</param>
        /// <param name="values">单个寄存器的值</param>
        /// <param name="unitId">单元标识符</param>
        public static (byte[], int) Get0x06RequestMessage(short transactionId, short addr, byte[] values, byte unitId = 1)
        {
            byte[] data = GetFinallyRequestMessage(transactionId, unitId, ModBusFunction.Wirte06, addr, values);
            return (data, data.Length);
        }

        /// <summary>
        /// 获取写多个线圈请求消息
        /// </summary>
        /// <param name="transactionId">事物处理标识</param>
        /// <param name="addr">线圈起始地址</param>
        /// <param name="isSets">多个线圈的数据(bool数组)</param>
        /// <param name="unitId">单元标识符</param>
        /// <returns></returns>
        public static (byte[], int) Get0x0FRequestMessage(short transactionId, short addr, bool[] isSets, byte unitId = 1)
        {
            byte[] values = ByteArrayLib.GetByteArrayFromBoolArray(isSets);
            int valuesCount = values.Length;
            byte[] ends = new byte[3 + valuesCount];
            byte[] nums = ByteArrayLib.GetByteArrayFromShort((short)isSets.Length);
            ends[0] = nums[0];
            ends[1] = nums[1];
            ends[2] = (byte)valuesCount;
            Array.Copy(values, 0, ends, 3, valuesCount);
            return (GetFinallyRequestMessage(transactionId, unitId, ModBusFunction.Wirte0F, addr, ends), 12);
        }

        /// <summary>
        /// 获取写多个寄存器请求消息
        /// </summary>
        /// <param name="transactionId">事物处理标识</param>
        /// <param name="addr">寄存器起始地址</param>
        /// <param name="values"></param>
        /// <param name="unitId">单元标识符</param>
        /// <returns></returns>
        public static (byte[], int) Get0x10RequestMessage(short transactionId, short addr, byte[] values, byte unitId = 1)
        {
            int len = values.Length;
            short count = (short)(len >> 1);
            byte[] ends = new byte[3 + len];
            ends[0] = (byte)(count >> 8);
            ends[1] = (byte)count;
            ends[2] = (byte)values.Length;
            Array.Copy(values, 0, ends, 3, len);
            return (GetFinallyRequestMessage(transactionId, unitId, ModBusFunction.Wirte10, addr, ends), 12);
        }
        #endregion

        /// <summary>
        /// 获取最终请求信息数据
        /// </summary>
        /// <param name="transactionId">事物处理标识</param>
        /// <param name="unitId">单元标识符</param>
        /// <param name="function">功能码</param>
        /// <param name="addr">地址</param>
        /// <param name="ends">数据</param>
        /// <returns></returns>
        private static byte[] GetFinallyRequestMessage(short transactionId, byte unitId, ModBusFunction function, short addr, byte[] ends)
        {
            byte[] buffer = new byte[10 + ends.Length];
            buffer[0] = (byte)(transactionId >> 8);
            buffer[1] = (byte)transactionId;
            //bufer[2]和bufer[3]默认值是0，代表TCP协议
            //length是buffer[5]之后的长度，其值=单元标识符+功能码+两字节地址+数据长度
            short length = (short)(4 + ends.Length);
            buffer[4] = (byte)(length >> 8);
            buffer[5] = (byte)length;
            buffer[6] = unitId;
            buffer[7] = (byte)function;
            buffer[8] = (byte)(addr >> 8);
            buffer[9] = (byte)addr;
            Array.Copy(ends, 0, buffer, 10, ends.Length);
            return buffer;
        }
        #endregion

        #region 分析响应消息
        public static ModBusAnalysis AnalysisResponseMessage(byte[] requestBuffer, byte[] responseBuffer, int count)
        {
            if (count < 9 || requestBuffer == null || responseBuffer == null || responseBuffer.Length != count)
            {
                return null;
            }

            for (int i = 0; i < 8; i++)
            {
                if (i != 4 && i != 5 && requestBuffer[i] != responseBuffer[i])
                {
                    return null;
                }
            }

            short transactionId = ShortLib.GetShortFromByteArray(responseBuffer, 0);
            byte unitId = responseBuffer[6];
            byte function = responseBuffer[7];
            byte[] data = null;
            if (function < 0x05)
            {
                byte length = responseBuffer[8];
                if (length == responseBuffer.Length - 9)
                {
                    data = new byte[length];
                    Array.Copy(responseBuffer, 9, data, 0, length);
                }
            }
            else
            {
                //暂不做写入响应分析,只截出除从设备ID，功能码，校验码之外的数据
                bool equal = true;
                for (int i = 8; i < 12; i++)
                {
                    if (responseBuffer[i] != requestBuffer[i])
                    {
                        equal = false;
                        break;
                    }
                }
                if (equal)
                {
                    data = new byte[responseBuffer.Length - 8];
                    Array.Copy(responseBuffer, 8, data, 0, data.Length);
                }
            }

            if (data == null)
            {
                return null;
            }

            ModBusAnalysis modBusAnalysis = new ModBusAnalysis
            {
                TransactionId = transactionId,
                Identifier = unitId,
                ModBusFunction = (ModBusFunction)function,
                Data = data
            };
            return modBusAnalysis;
        }
        #endregion
    }
}
